// problematic code
// 2020.10.25
// whsu

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#include <mpi.h>
#include <omp.h>

typedef long long ll;

unsigned int getRandomSeed() {
    unsigned int seed;
    FILE *fp = fopen("/dev/urandom", "rb");
    fread(&seed, sizeof(unsigned int), 1, fp);
    fclose(fp);
    return seed;
}

int main(int argc, char **argv) {

    MPI_Init(&argc, &argv);

    int processCount, processRank;
    ll totalHits, inCircleHits = 0;
    time_t start, end;
    // QUESTION what is MPI_COMM_WORLD
    MPI_Comm_size(MPI_COMM_WORLD, &processCount);
    MPI_Comm_rank(MPI_COMM_WORLD, &processRank);

    // Distribute Work
    if (processRank==0) {
        printf("Enter total hits:\n");
        scanf("%lld", &totalHits);

        start = time(NULL);

        ll taskDistribute = totalHits / processCount;
        totalHits = taskDistribute + totalHits%processCount;

        printf("Self (Process 0) shall do %lld calculations.\n", totalHits);
        // #pragma omp parallel for num_threads(10)
        for (int i=0 ; i<processCount-1 ; i++) {
            printf("Telling process %d: \"You shall do %lld calculations.\"\n", i, taskDistribute);
            fflush(stdout);
            MPI_Send(&taskDistribute, 1, MPI_LONG_LONG, i+1, 0, MPI_COMM_WORLD);
            printf("%d Distributed!\n", i+1);
            fflush(stdout);
        }
    } else {
        MPI_Status status;
        MPI_Recv(&totalHits, 1, MPI_LONG_LONG, 0, 0, MPI_COMM_WORLD, &status);
    }

    // work
    srand(getRandomSeed());
    // #pragma omp parallel for num_threads(4) reduction(+:inCircleHits)
    for (ll i=0 ; i<totalHits ; i++) {
        float x = 1.0f*rand()/RAND_MAX;
        float y = 1.0f*rand()/RAND_MAX;
        if (x*x+y*y <= 1) inCircleHits++;
    }

    // Merge
    if (processRank==0) {
        printf("Self: %lld hits in circle out of %lld hits totally.\n", inCircleHits, totalHits);

        ll resultTotal=0, resultIn=0;
        // #pragma omp parallel for num_threads(10) reduction(+:resultTotal) reduction(+:resultIn)
        for (int i=1 ; i<processCount ; i++) {
            MPI_Status status;
            ll receivedTotal, receivedIn;
            MPI_Recv(&receivedTotal, 1, MPI_LONG_LONG, i, 1, MPI_COMM_WORLD, &status);
            MPI_Recv(&receivedIn, 1, MPI_LONG_LONG, i, 2, MPI_COMM_WORLD, &status);
            printf("Process %d: %lld hits in circle out of %lld hits totally.\n", i, receivedIn, receivedTotal);

            resultIn += receivedIn;
            resultTotal += receivedTotal;
        }

        // add process 0's work
        resultIn += inCircleHits;
        resultTotal += totalHits;

        end = time(NULL);

        printf("Total: %lld hits in circle out of %lld hits totally.\n", resultIn, resultTotal);
        printf("PI=%f\nUsed time:%d\n", 4.0*resultIn/resultTotal, (int)(end-start));
    } else {
        MPI_Send(&totalHits, 1, MPI_LONG_LONG, 0, 1, MPI_COMM_WORLD);
        MPI_Send(&inCircleHits, 1, MPI_LONG_LONG, 0, 2, MPI_COMM_WORLD);
    }

    MPI_Finalize();
    return 0;

}

